/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.aopalliance.intercept.Interceptor;
import org.aopalliance.intercept.MethodInterceptor;

import org.springframework.aop.Advisor;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.lang.Nullable;

/**
 * A simple but definitive way of working out an advice chain for a Method,
 * given an {@link Advised} object. Always rebuilds each advice chain;
 * caching can be provided by subclasses.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @author Adrian Colyer
 * @since 2.0.3
 */
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

	@Override
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		/**
		 * config指的就是ProxyFactory
		 * 从ProxyFactory中拿到所有的Advisor（添加时被封装成了DefaultPointcutAdvisor）
		 * 添加的时候会控制顺序
		 */
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		// 遍历所有的Advisor
		for (Advisor advisor : advisors) {
			// 如果当前advisor属于PointcutAdvisor类型
			if (advisor instanceof PointcutAdvisor) {
				/**
				 * 举个例子：
				 * public static void main(String[] args) {
				 *         ProxyFactory proxyFactory = new ProxyFactory();
				 *         // 表示现在定义的这个Advisor，只要被代理类是UserService，那么其所有方法都可以走代理逻辑
				 *         proxyFactory.addAdvisor(new PointcutAdvisor() {
				 *             @Override
				 *             public Pointcut getPointcut() {
				 *                 return new Pointcut() {
				 *                     @Override
				 *                     public ClassFilter getClassFilter() {
				 *                         return new ClassFilter() {
				 *                             @Override
				 *                             public boolean matches(Class<?> clazz) {
				 *                                 return clazz.equals(UserService.class);
				 *                             }
				 *                         };
				 *                     }
				 *
				 *                     @Override
				 *                     public MethodMatcher getMethodMatcher() {
				 *                         return new MethodMatcher() {
				 *                             @Override
				 *                             public boolean matches(Method method, Class<?> targetClass) {
				 *                                 return method.getName().equals("test");
				 *                             }
				 *
				 *                             @Override
				 *                             public boolean isRuntime() {
				 *                                 return false;
				 *                             }
				 *
				 *                             @Override
				 *                             public boolean matches(Method method, Class<?> targetClass, Object... args) {
				 *                                 return false;
				 *                             }
				 *                         };
				 *                     }
				 *                 };
				 *             }
				 *
				 *             @Override
				 *             public Advice getAdvice() {
				 *                 return new XiexuBeforeAdvice();
				 *             }
				 *
				 *             @Override
				 *             public boolean isPerInstance() {
				 *                 return false;
				 *             }
				 *         });
				 *     }
				 */
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				// 先匹配类
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {

					// 再匹配方法
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}

					if (match) {
						/**
						 * 如果匹配则将Advisor里面的Advice封装成MethodInterceptor
						 * 注意：一个Advisor其实只会对应一个MethodInterceptor
						 * 当前Advisor中的Advice可能即是MethodBeforeAdvice，也是ThrowsAdvice
						 * 点进getInterceptors()看看
						 */
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						// 如果方法匹配器的isRuntime为true
						if (mm.isRuntime()) {
							// 遍历所有匹配的MethodInterceptor
							for (MethodInterceptor interceptor : interceptors) {
								// 将其封装成InterceptorAndDynamicMethodMatcher并添加到interceptorList集合中
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}

					/**
					 * 最终，interceptorList中存储的是当前正在执行的Method所匹配的MethodInterceptor，可能动态的，也可能是非动态的，
					 * 找到Method所匹配的MethodInterceptor后，就会开始调用这些MethodInterceptor，如果是动态的，会额外进行方法参数的匹配
					 */
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				// 将Advisor封装成为Interceptor
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

	/**
	 * Determine whether the Advisors contain matching introductions.
	 */
	private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
		for (Advisor advisor : advisors) {
			if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (ia.getClassFilter().matches(actualClass)) {
					return true;
				}
			}
		}
		return false;
	}

}
